package com.hs.myshell.edit;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * @see org.eclipse.tm.internal.terminal.textcanvas.PipedInputStream
 * @author Administrator
 *
 */
public class PipedInputStream extends InputStream {
    protected final OutputStream fOutputStream = new PipedInputStream.PipedOutputStream();
    private final PipedInputStream.BoundedByteBuffer fQueue;

    public PipedInputStream(int bufferSize) {
        this.fQueue = new PipedInputStream.BoundedByteBuffer(bufferSize);
    }

    public OutputStream getOutputStream() {
        return this.fOutputStream;
    }

    public void waitForAvailable(long millis) throws InterruptedException {
        synchronized(this.fQueue) {
            if (this.fQueue.available() == 0 && !this.fQueue.fClosed) {
                this.fQueue.wait(millis);
            }

        }
    }

    public int available() {
        synchronized(this.fQueue) {
            return this.fQueue.available();
        }
    }

    public int read() throws IOException {
        try {
            synchronized(this.fQueue) {
                return this.fQueue.read();
            }
        } catch (InterruptedException var3) {
            Thread.currentThread().interrupt();
            return -1;
        }
    }

    public void close() throws IOException {
        synchronized(this.fQueue) {
            this.fQueue.close();
        }
    }

    public int read(byte[] cbuf, int off, int len) throws IOException {
        int n = 0;
        if (len == 0) {
            return 0;
        } else {
            try {
                synchronized(this.fQueue) {
                    int nn;
                    if (this.fQueue.available() == 0) {
                        nn = this.fQueue.read();
                        if (nn == -1) {
                            return -1;
                        }

                        cbuf[off] = (byte)nn;
                        ++n;
                    }

                    if (n < len && this.fQueue.available() > 0) {
                        nn = Math.min(this.fQueue.available(), len - n);
                        if (nn == 0 && this.fQueue.isClosed()) {
                            if (n == 0) {
                                n = -1;
                            }

                            return n;
                        }

                        this.fQueue.read(cbuf, off + n, nn);
                        n += nn;
                    }
                }
            } catch (InterruptedException var8) {
                Thread.currentThread().interrupt();
            }

            return n;
        }
    }

    private class BoundedByteBuffer {
        protected final byte[] fBuffer;
        protected int fPutPos = 0;
        protected int fTakePos = 0;
        protected int fUsedSlots = 0;
        private boolean fClosed;

        public BoundedByteBuffer(int capacity) throws IllegalArgumentException {
            if (capacity <= 0) {
                throw new IllegalArgumentException();
            } else {
                this.fBuffer = new byte[capacity];
            }
        }

        public int available() {
            return this.fUsedSlots;
        }

        public void write(byte b) throws InterruptedException {
            while(this.fUsedSlots == this.fBuffer.length) {
                this.wait();
            }

            this.fBuffer[this.fPutPos] = b;
            this.fPutPos = (this.fPutPos + 1) % this.fBuffer.length;
            if (this.fUsedSlots++ == 0) {
                this.notifyAll();
            }

        }

        public int getFreeSlots() {
            return this.fBuffer.length - this.fUsedSlots;
        }

        public void write(byte[] b, int off, int len) throws InterruptedException {
            assert len <= this.getFreeSlots();

            while(this.fUsedSlots == this.fBuffer.length) {
                this.wait();
            }

            int n = Math.min(len, this.fBuffer.length - this.fPutPos);
            System.arraycopy(b, off, this.fBuffer, this.fPutPos, n);
            if (this.fPutPos + len > this.fBuffer.length) {
                System.arraycopy(b, off + n, this.fBuffer, 0, len - n);
            }

            this.fPutPos = (this.fPutPos + len) % this.fBuffer.length;
            boolean wasEmpty = this.fUsedSlots == 0;
            this.fUsedSlots += len;
            if (wasEmpty) {
                this.notifyAll();
            }

        }

        public int read() throws InterruptedException {
            while(this.fUsedSlots == 0) {
                if (this.fClosed) {
                    return -1;
                }

                this.wait();
            }

            byte b = this.fBuffer[this.fTakePos];
            this.fTakePos = (this.fTakePos + 1) % this.fBuffer.length;
            if (this.fUsedSlots-- == this.fBuffer.length) {
                this.notifyAll();
            }

            return b;
        }

        public int read(byte[] cbuf, int off, int len) throws InterruptedException {
            assert len <= this.available();

            while(this.fUsedSlots == 0) {
                if (this.fClosed) {
                    return 0;
                }

                this.wait();
            }

            int n = Math.min(len, this.fBuffer.length - this.fTakePos);
            System.arraycopy(this.fBuffer, this.fTakePos, cbuf, off, n);
            if (this.fTakePos + len > n) {
                System.arraycopy(this.fBuffer, 0, cbuf, off + n, len - n);
            }

            this.fTakePos = (this.fTakePos + len) % this.fBuffer.length;
            boolean wasFull = this.fUsedSlots == this.fBuffer.length;
            this.fUsedSlots -= len;
            if (wasFull) {
                this.notifyAll();
            }

            return len;
        }

        public void close() {
            this.fClosed = true;
            this.notifyAll();
        }

        public boolean isClosed() {
            return this.fClosed;
        }
    }

    class PipedOutputStream extends OutputStream {
        PipedOutputStream() {
        }

        public void write(byte[] b, int off, int len) throws IOException {
            try {
                synchronized(PipedInputStream.this.fQueue) {
                    if (PipedInputStream.this.fQueue.isClosed()) {
                        throw new IOException("Stream is closed!");
                    }

                    int written = 0;

                    while(written < len) {
                        if (PipedInputStream.this.fQueue.getFreeSlots() == 0) {
                            PipedInputStream.this.fQueue.write(b[off + written]);
                            ++written;
                        } else {
                            int n = Math.min(PipedInputStream.this.fQueue.getFreeSlots(), len - written);
                            PipedInputStream.this.fQueue.write(b, off + written, n);
                            written += n;
                        }
                    }
                }
            } catch (InterruptedException var8) {
                Thread.currentThread().interrupt();
            }

        }

        public void write(int b) throws IOException {
            try {
                synchronized(PipedInputStream.this.fQueue) {
                    if (PipedInputStream.this.fQueue.isClosed()) {
                        throw new IOException("Stream is closed!");
                    }

                    PipedInputStream.this.fQueue.write((byte)b);
                }
            } catch (InterruptedException var4) {
                Thread.currentThread().interrupt();
            }

        }

        public void close() throws IOException {
            synchronized(PipedInputStream.this.fQueue) {
                PipedInputStream.this.fQueue.close();
            }
        }
    }
}